#ifndef RBT_MULTI_THREAD_SYNC
#define RBT_MULTI_THREAD_SYNC

#include "../typedef.h"
#include "../Text/Text.h"

//Mutex return value
RETURNCODE E_MUTEX_ALREADY_EXIST			= -601;
RETURNCODE E_MUTEX_NOT_FOUND			    = -602;
RETURNCODE E_MUTEX_WAIT_TIMEOUT				= -603;
RETURNCODE E_MUTEX_WAIT_FAILED			    = -604;

//Event return value
RETURNCODE E_EVENT_ALREADE_EXIST		    = -605;
RETURNCODE E_EVENT_WAIT_TIMEOUT				= -606;
RETURNCODE E_EVENT_WAIT_FAILED				= -607;

//Semaphore return value
RETURNCODE E_SEMAPHORE_PARAM_INCORRECT		= -608;
RETURNCODE E_SEMAPHORE_CANT_GET_LOCK_COUNT	= -609;

#define LockScope(x) ScopeLock locScope##x##(x)

namespace MultiThread
{
	class SyncObject
	{
	public:
		virtual int32 Lock() = 0;
		virtual int32 Unlock() = 0;
	};

	class SyncObjectWindows : public SyncObject
	{
	public:
		virtual int32 Lock() = 0;
		virtual int32 Unlock() = 0;

		virtual operator SyncObject* () const
		{
			return (SyncObject*)this;
		}

		virtual operator HANDLE () const
		{
			return m_hObject;
		}

		SyncObjectWindows() : m_hObject(INVALID_HANDLE_VALUE)
		{

		}

		virtual ~SyncObjectWindows()
		{

		}

	protected:
		HANDLE m_hObject;
	};

	class CriticalSection : public SyncObjectWindows
	{
	public:
		int32 Initialize()
		{
			InitializeCriticalSection(&m_criticalSection);
			m_hObject = m_criticalSection.LockSemaphore;

			return R_SUCCEED;
		}

		int32 Uninitialize()
		{
			DeleteCriticalSection(&m_criticalSection);

			return R_SUCCEED;
		}

		int32 Lock()
		{
			EnterCriticalSection(&m_criticalSection);
			m_isLocked = true;

			return R_SUCCEED;
		}

		int32 TryLock()
		{
			if (TryEnterCriticalSection(&m_criticalSection))
			{
				m_isLocked = true;
				return R_SUCCEED;
			}
			
			return R_FAILED;
		}

		int32 Unlock()
		{
			m_isLocked = false;
			LeaveCriticalSection(&m_criticalSection);

			return R_SUCCEED;
		}

		int32 IsLock()
		{
			return m_isLocked ? R_SUCCEED : R_FAILED;
		}

		//Constructor and Destructor
		CriticalSection() : m_isLocked(false)
		{
			Initialize();
		}

		virtual ~CriticalSection()
		{
			if (IsLock() == R_SUCCEED)
			{
				Unlock();
			}

			Uninitialize();
		}

	protected:
		CRITICAL_SECTION m_criticalSection;
		bool m_isLocked;
	};

	class ScopeLock 
	{
	public:
		ScopeLock(SyncObject& object) : m_object(object) 
		{
			m_object.Lock(); 
		}

		~ScopeLock()
		{
			m_object.Unlock(); 
		}

		int32 Lock(SyncObject& object)
		{
			return object.Lock();
		}

		int32 Unlock(SyncObject& object)
		{
			return object.Unlock();
		}

	protected:
		SyncObject& m_object;
	};

	class Mutex : public SyncObjectWindows
	{
	public:

		enum MUTEX_ACCESS_MODE
		{
			MODE_ALL_ACCESS,
			MODE_MODIFY_STATE,
			MODE_SYNCHRONIZE
		};

		int32 Initialize(tchar* mutexName, bool initialOwner = false)
		{
			m_hObject = CreateMutex(NULL, initialOwner, mutexName);

			if (m_hObject == NULL)
			{
				m_hObject = INVALID_HANDLE_VALUE;
				return R_FAILED;
			}

			if (GetLastError() == ERROR_ALREADY_EXISTS)
			{
				return E_MUTEX_ALREADY_EXIST;
			}
			
			m_mutexAccessMode = MODE_ALL_ACCESS;

			return R_SUCCEED;
		}

		int32 Uninitialize()
		{
			if (!CloseHandle(m_hObject))
			{
				return R_FAILED;
			}

			return R_SUCCEED;
		}

		int32 Release()
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			if (!ReleaseMutex(m_hObject))
			{
				return R_FAILED;
			}

			return R_SUCCEED;
		}

		int32 Open(MUTEX_ACCESS_MODE mutexAccessMode, bool inheriteHandle, tchar* mutexName)
		{
			DWORD dwAccessMode = 0;
			if (mutexAccessMode == MODE_ALL_ACCESS)
			{
				dwAccessMode = MUTEX_ALL_ACCESS;
			}
			else if (mutexAccessMode == MODE_MODIFY_STATE)
			{
				dwAccessMode = MUTEX_MODIFY_STATE;
			}
			else
			{
				dwAccessMode = SYNCHRONIZE;
			}

			m_hObject = OpenMutex(dwAccessMode, inheriteHandle, mutexName);

			if (m_hObject == NULL)
			{
				m_hObject = INVALID_HANDLE_VALUE;
				return R_FAILED;
			}

			if (GetLastError() == ERROR_FILE_NOT_FOUND)
			{
				return E_MUTEX_NOT_FOUND;
			}

			m_mutexAccessMode = mutexAccessMode;

			return R_SUCCEED;
		}

		int32 Lock()
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			WaitForSingleObject(m_hObject, INFINITE);
			m_isLocked = true;

			return R_SUCCEED;
		}

		int32 Lock(uint32 milliSeconds)
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			DWORD dwRet = WaitForSingleObject(m_hObject, milliSeconds);

			m_isLocked = false;

			if (dwRet == WAIT_OBJECT_0)
			{
				m_isLocked = true;
				return R_SUCCEED;
			}
			else if (dwRet == WAIT_TIMEOUT)
			{
				return E_MUTEX_WAIT_TIMEOUT;
			}
			else if (dwRet == WAIT_FAILED)
			{
				return E_MUTEX_WAIT_FAILED;
			}

			return R_FAILED;
		}

		int32 Unlock()
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			if (m_isLocked)
			{
				m_isLocked = false;

				if (!ReleaseMutex(m_hObject))
				{
					return R_FAILED;
				}

				return R_SUCCEED;
			}
			
			return R_FAILED;
		}

		int32 IsLock()
		{
			return m_isLocked ? R_SUCCEED : R_FAILED;
		}

		//Constructor and Destructor
		Mutex() : m_isLocked(false)
		{
			m_hObject = INVALID_HANDLE_VALUE;
		}

		Mutex(tchar* mutexName, bool initialOwner = false) : m_isLocked(false)
		{
			m_hObject = INVALID_HANDLE_VALUE;

			if (Open(MODE_ALL_ACCESS, true, mutexName) != R_SUCCEED)
			{
				if (Initialize(mutexName, initialOwner) != R_SUCCEED)
				{
					m_hObject = INVALID_HANDLE_VALUE;
				}
			}
		}

		virtual ~Mutex()
		{
			Uninitialize();
		}

	protected:
		MUTEX_ACCESS_MODE m_mutexAccessMode;
		bool m_isLocked;
	};

	class Event : public SyncObjectWindows
	{
	public:
	
		enum EVENT_ACCESS_MODE
		{
			MODE_ALL_ACCESS,
			MODE_MODIFY_STATE,
			MODE_SYNCHRONIZE
		};

		int32 Initialize(tchar* eventName, bool initialState = true, bool manualReset = false)
		{
			m_hObject = CreateEvent(NULL, manualReset, initialState, eventName);

			if (m_hObject == NULL)
			{
				m_hObject = INVALID_HANDLE_VALUE;
				return R_FAILED;
			}

			if (GetLastError() == ERROR_ALREADY_EXISTS)
			{
				return E_EVENT_ALREADE_EXIST;
			}

			m_eventAccessMode = MODE_ALL_ACCESS;

			return R_SUCCEED;
		}

		int32 Uninitialize()
		{
			if (!CloseHandle(m_hObject))
			{
				return R_FAILED;
			}

			return R_SUCCEED;
		}

		int32 Open(EVENT_ACCESS_MODE eventAccessMode, bool inheriteHandle, tchar* eventName)
		{
			DWORD dwAccessMode = 0;
			if (eventAccessMode == MODE_ALL_ACCESS)
			{
				dwAccessMode = EVENT_ALL_ACCESS;
			}
			else if (eventAccessMode == MODE_MODIFY_STATE)
			{
				dwAccessMode = EVENT_MODIFY_STATE;
			}
			else
			{
				dwAccessMode = SYNCHRONIZE;
			}

			m_hObject = OpenEvent(dwAccessMode, inheriteHandle, eventName);

			if (m_hObject == NULL)
			{
				m_hObject = INVALID_HANDLE_VALUE;
				return R_FAILED;
			}

			m_eventAccessMode = eventAccessMode;

			return R_SUCCEED;

		}

		int32 Pulse()
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			if (!PulseEvent(m_hObject))
			{
				return R_FAILED;
			}

			return R_SUCCEED;
		}

		int32 Set()
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			if (!SetEvent(m_hObject))
			{
				return R_FAILED;
			}

			return R_SUCCEED;
		}

		int32 Reset()
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			if (!ResetEvent(m_hObject))
			{
				return R_FAILED;
			}

			return R_SUCCEED;
		}

		int32 Lock()
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			WaitForSingleObject(m_hObject, INFINITE);
			m_isLocked = true;

			return R_SUCCEED;
		}

		int32 Lock(uint32 milliSeconds)
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			DWORD dwRet = WaitForSingleObject(m_hObject, milliSeconds);

			m_isLocked = false;

			if (dwRet == WAIT_OBJECT_0)
			{
				m_isLocked = true;
				return R_SUCCEED;
			}
			else if (dwRet == WAIT_TIMEOUT)
			{
				return E_EVENT_WAIT_TIMEOUT;
			}
			else if (dwRet == WAIT_FAILED)
			{
				return E_EVENT_WAIT_FAILED;
			}

			return R_FAILED;
		}

		int32 Unlock()
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			if (m_isLocked)
			{
				m_isLocked = false;

				if (Set() != R_SUCCEED)
				{
					return R_FAILED;
				}

				return R_SUCCEED;
			}

			return R_FAILED;
		}

		int32 IsLock()
		{
			return m_isLocked ? R_SUCCEED : R_FAILED;
		}

		Event() : m_isLocked(false)
		{
			m_hObject = INVALID_HANDLE_VALUE;
		}

		Event(tchar* mutexName, bool initialState = true, bool manualReset = false) : m_isLocked(false)
		{
			m_hObject = INVALID_HANDLE_VALUE;

			if (Open(MODE_ALL_ACCESS, true, mutexName) != R_SUCCEED)
			{
				if (Initialize(mutexName, initialState, manualReset) != R_SUCCEED)
				{
					m_hObject = INVALID_HANDLE_VALUE;
				}
			}
		}

		virtual ~Event()
		{
			Uninitialize();
		}

	protected:
		EVENT_ACCESS_MODE m_eventAccessMode;
		bool m_isLocked;
	};

	class Semaphore : public SyncObjectWindows
	{
	public:
		enum SEMAPHORE_ACCESS_MODE
		{
			MODE_ALL_ACCESS,
			MODE_MODIFY_STATE,
			MODE_SYNCHRONIZE
		};

		int32 Initialize(tchar* mutexName, int32 initialSrcCount, int32 maxSrcCount)
		{
			if (initialSrcCount > maxSrcCount || initialSrcCount < 0 || maxSrcCount < 0)
			{
				return E_SEMAPHORE_PARAM_INCORRECT;
			}

			m_hObject = CreateSemaphore(NULL, initialSrcCount, maxSrcCount, mutexName);

			if (m_hObject == NULL)
			{
				m_hObject = INVALID_HANDLE_VALUE;
				return R_FAILED;
			}

			if (GetLastError() == ERROR_ALREADY_EXISTS)
			{
				return E_MUTEX_ALREADY_EXIST;
			}

			m_semaphoreAccessMode = MODE_ALL_ACCESS;
			m_curSrcCount = initialSrcCount;
			m_maxSrcCount = maxSrcCount;

			return R_SUCCEED;
		}

		int32 Uninitialize()
		{
			if (!CloseHandle(m_hObject))
			{
				return R_FAILED;
			}

			return R_SUCCEED;
		}

		int32 Release(int32 releaseCount, int32* previouseCount)
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			if (!ReleaseSemaphore(m_hObject, releaseCount, (LPLONG)previouseCount))
			{
				return R_FAILED;
			}

			return R_SUCCEED;
		}

		int32 Open(SEMAPHORE_ACCESS_MODE semaphoreAccessMode, bool inheriteHandle, tchar* mutexName)
		{
			DWORD dwAccessMode = 0;
			if (semaphoreAccessMode == MODE_ALL_ACCESS)
			{
				dwAccessMode = SEMAPHORE_ALL_ACCESS;
			}
			else if (semaphoreAccessMode == MODE_MODIFY_STATE)
			{
				dwAccessMode = SEMAPHORE_MODIFY_STATE;
			}
			else
			{
				dwAccessMode = SYNCHRONIZE;
			}

			m_hObject = OpenSemaphore(dwAccessMode, inheriteHandle, mutexName);

			if (m_hObject == NULL)
			{
				m_hObject = INVALID_HANDLE_VALUE;
				return R_FAILED;
			}

			m_semaphoreAccessMode = semaphoreAccessMode;
			m_semaphoreOpen = true;

			return R_SUCCEED;
		}

		int32 Lock()
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			WaitForSingleObject(m_hObject, INFINITE);
			--m_curSrcCount;

			return R_SUCCEED;
		}

		int32 Lock(uint32 milliSeconds)
		{
			if (m_hObject == INVALID_HANDLE_VALUE)
			{
				return R_FAILED;
			}

			DWORD dwRet = WaitForSingleObject(m_hObject, milliSeconds);

			if (dwRet == WAIT_OBJECT_0)
			{
				--m_curSrcCount;
				return R_SUCCEED;
			}
			else if (dwRet == WAIT_TIMEOUT)
			{
				return E_MUTEX_WAIT_TIMEOUT;
			}
			else if (dwRet == WAIT_FAILED)
			{
				return E_MUTEX_WAIT_FAILED;
			}

			return R_FAILED;
		}

		int32 Unlock()
		{
			if (Release(1, NULL) != R_SUCCEED)
			{
				return R_FAILED;
			}

			++m_curSrcCount;
			return R_SUCCEED;
		}

		int32 IsLock()
		{
			if (m_semaphoreOpen)
			{
				return E_SEMAPHORE_CANT_GET_LOCK_COUNT;
			}

			return m_curSrcCount == 0 ? R_SUCCEED : R_FAILED;
		}

		//Constructor and Destructor
		Semaphore() : m_curSrcCount(0), m_maxSrcCount(0), m_semaphoreOpen(false)
		{
			m_hObject = INVALID_HANDLE_VALUE;
		}

		Semaphore(tchar* mutexName, int32 initialSrcCount, int32 maxSrcCount) : m_curSrcCount(0), m_maxSrcCount(0), m_semaphoreOpen(false)
		{
			m_hObject = INVALID_HANDLE_VALUE;

			if (Open(MODE_ALL_ACCESS, true, mutexName) != R_SUCCEED)
			{
				if (Initialize(mutexName, initialSrcCount, maxSrcCount) != R_SUCCEED)
				{
					m_hObject = INVALID_HANDLE_VALUE;
				}
			}
		}

		virtual ~Semaphore()
		{
			Uninitialize();
		}

	protected:
		SEMAPHORE_ACCESS_MODE m_semaphoreAccessMode;
		int32 m_curSrcCount;
		int32 m_maxSrcCount;
		bool m_semaphoreOpen;
	};

	template<class T>
	class ThreadSafeType
	{
	public:
		T SetValue(T value)
		{
			ScopeLock scopeLock(m_criticalSection);
			m_value = value;
			return m_value;
		}

		T& GetValue()
		{
			ScopeLock scopeLock(m_criticalSection);
			return m_value;
		}

		const T& GetValue() const
		{
			ScopeLock scopeLock(m_criticalSection);
			return m_value;
		}

		operator T () const
		{
			return GetValue();
		}

		T operator = (T value) 
		{ 
			return SetValue(value); 
		}


		ThreadSafeType()
		{

		}

		ThreadSafeType(const T& value)
		{
			m_value = value;
		}

	protected:
		T m_value;
		mutable CriticalSection m_criticalSection;
	};
}

#endif